#!/usr/bin/env python
# coding:utf-8

import  os, base64, json
import shlex
import datetime
import subprocess
import time
import pwd
import shutil
import math

def ExecShell(cmdstring, cwd=None, timeout=None, shell=True):
    if shell:
        cmdstring_list = cmdstring
    else:
        cmdstring_list = shlex.split(cmdstring)
    if timeout:
        end_time = datetime.datetime.now() + datetime.timedelta(seconds=timeout)

    sub = subprocess.Popen(cmdstring_list, cwd=cwd, stdin=subprocess.PIPE, shell=shell, bufsize=4096,
                           stdout=subprocess.PIPE, stderr=subprocess.PIPE)

    while sub.poll() is None:
        time.sleep(0.1)
        if timeout:
            if end_time <= datetime.datetime.now():
                raise Exception("Timeout：%s" % cmdstring)

    return sub.communicate()

publist_mes={
    "PAGE": {"PREV": "上一页", "NEXT": "下一页", "START": "首页", "END": "尾页", "COUNT_START": "共", "COUNT_END": "条数据",
             "FO": "从", "LINE": "条"},
	"ERROR":"操作失败",
	"SUCCESS":"操作成功",
	"START":"启动",
	"STOP":"停止",
	"OFF":"停用",
	"ON":"启用",
	"OPEN":"打开",
	"CLOSE":"关闭",
	"ADD_SUCCESS":"添加成功",
	"ADD_ERROR":"添加失败",
	"DEL_SUCCESS":"删除成功",
	"DEL_ERROR":"删除失败",
	"SET_SUCCESS":"设置成功",
	"SET_ERROR":"设置失败",
	"EDIT_SUCCESS":"修改成功",
	"EDIT_ERROR":"修改失败",
	"CONF_ERROR":"配置文件错误",
	"PHP_GET_ERR":"PHP版本获取失败!",
	"PHP_NOT_EXISTS":"指定PHP版本不存在!",
	"DIR_EMPTY":"目录不能为空!",
	"PATH_ERROR":"不能以系统关键目录作为站点目录",
	"BACKUP_SUCCESS":"备份成功!",
	"UPDATE_SUCCESS":"更新成功!",
	"PANEL_UPDATE":"成功升级到{1}",
	"PANEL_UPDATE_ERR_DOWN":"文件下载失败，请重试或在命令行手动升级!",
	"PANEL_UPDATE_ERR_NEW":"当前已是最新版本!",
	"PANEL_UPDATE_MSG":"当前为正式版，已是最新版本，正式版更新周期一般一两个月更新一次，内测版每周三更新!",
	"PANEL_UPDATE_MSG_TEST":"当前为内测版，已是最新版本，正式版更新周期一般一两个月更新一次，内测版每周三更新!",
	"EXEC_ERR_TASK":"请等待所有安装任务完成再执行!",
	"CONNECT_ERR":"连接服务器失败!",
	"INPUT_PS":"填写备注",
	"FILE_NOT_EXISTS":"文件不存在!",
	"PANEL_SAVE":"配置已保存",
	"PHP_UPLOAD_MAX_ERR":"上传大小限制不能小于2MB",
	"PHP_TIMEOUT_ERR":"请填写30-86400间的值!",
	"DATE_SUCCESS":"同步服务器时间成功!",
	"CONTROL_CLOSE":"监控服务已关闭!",
	"CONTROL_ERR":"保存天数不合法!",
	"FILE_UPLOAD_SUCCESS":"上传成功!",
	"FILE_EXISTS":"指定文件已存在!",
	"DIR_EXISTS":"指定目录已存在!",
	"DIR_NOT_EXISTS":"指定目录不存在!",
	"DIR_ERR_NOT_EMPTY":"不能删除非空目录!",
	"FILE_DANGER":"请不要花样作死",
	"FILE_CREATE_SUCCESS":"文件创建成功!",
	"FILE_CREATE_ERR":"文件创建失败!",
	"DIR_CREATE_SUCCESS":"目录创建成功!",
	"DIR_CREATE_ERR":"目录创建失败!",
	"DIR_MOVE_RECYCLE_BIN":"已将目录移动到回收站!",
	"FILE_MOVE_RECYCLE_BIN":"已将文件移动到回收站!",
	"DIR_DEL_SUCCESS":"删除目录成功!",
	"DIR_DEL_ERR":"删除目录失败!",
	"FILE_DEL_SUCCESS":"删除文件成功!",
	"FILE_DEL_ERR":"删除文件失败!",
	"FILE_RE_RECYCLE_BIN_ERR":"恢复失败!",
	"FILE_RE_RECYCLE_BIN":"恢复成功!",
	"FILE_DEL_RECYCLE_BIN":"已彻底从回收站删除{1}!",
	"FILE_CLOSE_RECYCLE_BIN":"已清空回收站!",
	"FILE_OFF_RECYCLE_BIN":"已关闭回收站功能!",
	"FILE_ON_RECYCLE_BIN":"已开启回收站功能!",
	"FILE_COPY_SUCCESS":"文件复制成功!",
	"FILE_COPY_ERR":"文件复制失败!",
	"DIR_COPY_SUCCESS":"目录复制成功!",
	"DIR_COPY_ERR":"目录复制失败!",
	"MOVE_SUCCESS":"移动文件或目录成功!",
	"MOVE_ERR":"移动文件或目录失败!",
	"FILE_GET_ERR":"文件内容获取失败,格式不被支持!",
	"FILE_SAVE_SUCCESS":"文件已保存!",
	"FILE_SAVE_ERR":"文件保存失败!",
	"ZIP_SUCCESS":"文件压缩成功!",
	"ZIP_ERR":"文件压缩失败!",
	"UNZIP_SUCCESS":"文件解压成功!",
	"UNZIP_ERR":"文件解压失败!",
	"FILE_ALL_TIPS":"标记成功,请在目标目录点击粘贴所有按钮!",
	"FILE_ALL_ACCESS":"批量设置权限成功!",
	"FILE_ALL_DEL":"批量删除成功!",
	"FILE_ALL":"批量操作成功[{1}],失败[{2}]",
	"FILE_DOANLOAD":"已将下载任务添加到队列!",
	"FILE_SHELL_ERR":"抱歉,不支持[{1}]命令!",
	"FILE_SHELL_EXEC":"命令已发送",
	"FILE_SHELL_EMPTY":"当前没有SHELL在执行!",
	"FIREWALL_IP_FORMAT":"您输入的IP地址不合法!",
	"FIREWALL_IP_EXISTS":"您要放屏蔽的IP已存在屏蔽列表，无需重复处理!",
	"FIREWALL_PORT_EXISTS":"您要放行的端口已存在，无需重复放行!",
	"FIREWALL_PORT_PANEL":"失败，不能删除当前面板端口!",
	"FIREWALL_SSH_STOP":"SSH服务已停用",
	"FIREWALL_SSH_START":"SSH服务已启用",
	"FIREWALL_SSH_PORT_ERR":"端口范围必需在22-65535之间!",
	"FIREWALL_SSH_PORT_EXISTS":"指定端口为常用端口，请换一个试试!",
	"FTP_USERNAME_ERR_T":"用户名不合法,不能带有特殊符号!",
	"FTP_USERNAME_ERR_LEN":"用户名不合法,不能少于3个字符!",
	"FTP_USERNAME_ERR_DIR":"不能以系统关键目录作为FTP目录!",
	"FTP_USERNAME_ERR_EXISTS":"用户[{1}]已存在!",
	"FTP_STATUS_ERR":"{1}FTP用户失败!",
	"FTP_PORT_PS":"FTP端口",
	"PAGE":{"PREV":"上一页","NEXT":"下一页","START":"首页","END":"尾页","COUNT_START":"共","COUNT_END":"条数据","FO":"从","LINE":"条"},
	"SSL_CHECK_WRITE_ERR":"验证信息写入失败!",
	"SSL_CHECK_DNS_ERR":"以下域名解析不正确，或解析未生效!<li style='color:red;'>{1}</li>",
	"SSL_GET_SUCCESS":"获取成功!",
	"SSL_NOT_BTUSER":"请绑定宝塔帐户!",
	"SSL_BTUSER_UN":"已解除绑定!",
	"WAF_NOT_NGINX":"当前只支持Nginx",
	"WAF_NOT_NGINX_VERSION":"您当前的Nginx未安装WAF支持，请重新安装Nginx!",
	"WAF_UPDATE":"更新成功!",
	"WAF_CONF_NOT_EXISTS":"失败，指定配置值不存在!",
	"WAF_CONF_EXISTS":"失败，指定配置值已存在!",
	"WAF_CONF_ERR":"失败，设置值不合法!",
	"CODE_ERR":"验证码错误,请重新输入!",
	"CODE_TIMEOUT":"验证码已过期,请点验证码图片重新获取!",
	"CODE_NOT_EXISTS":"验证码不存在!",
	"REQUEST_ERR":"错误的请求!",
	"RECYCLE_BIN_DB":"已成功将数据库移动到回收站!",
	"RECYCLEDB":"已恢复数据库!",
	"UNZIP_ERR_DIR":"不能在关键目录解压文件,这会导致系统目录的权限被替换.",

}
def getMsg(key, args=()):
    try:
        logMessage = publist_mes
        keys = logMessage.keys()
        msg = None
        if key in keys:
            msg = logMessage[key]
            for i in range(len(args)):
                rep = '{' + str(i + 1) + '}'
                msg = msg.replace(rep, args[i])
        return msg
    except:
        return key


def returnMsg(status, msg, args=()):

    # 取通用字曲返回
    logMessage =publist_mes
    keys = logMessage.keys()
    if msg in keys:
        msg = logMessage[msg]
        for i in range(len(args)):
            rep = '{' + str(i + 1) + '}'
            msg = msg.replace(rep, args[i])
    return {'status': status, 'msg': msg}

class Page():
    # --------------------------
    # 分页类 - JS回调版
    # --------------------------
    __PREV = '上一页'
    __NEXT = '下一页'
    __START = '首页'
    __END = '尾页'
    __COUNT_START = '共'
    __COUNT_END = '条数据'
    __FO = '从'
    __LINE = '条'
    __LIST_NUM = 4
    SHIFT = None  # 偏移量
    ROW = None  # 每页行数
    __C_PAGE = None  # 当前页
    __COUNT_PAGE = None  # 总页数
    __COUNT_ROW = None  # 总行数
    __URI = None  # URI
    __RTURN_JS = False  # 是否返回JS回调
    __START_NUM = None  # 起始行
    __END_NUM = None  # 结束行

    def __init__(self):
        tmp = getMsg('PAGE')
        if tmp:
            self.__PREV = tmp['PREV']
            self.__NEXT = tmp['NEXT']
            self.__START = tmp['START']
            self.__END = tmp['END']
            self.__COUNT_START = tmp['COUNT_START']
            self.__COUNT_END = tmp['COUNT_END']
            self.__FO = tmp['FO']
            self.__LINE = tmp['LINE']

    def GetPage(self, pageInfo, limit='1,2,3,4,5,6,7,8'):
        # 取分页信息
        # @param pageInfo 传入分页参数字典
        # @param limit 返回系列
        self.__RTURN_JS = pageInfo['return_js']
        self.__COUNT_ROW = pageInfo['count']
        self.ROW = pageInfo['row']
        self.__C_PAGE = self.__GetCpage(pageInfo['p'])
        self.__START_NUM = self.__StartRow()
        self.__END_NUM = self.__EndRow()
        self.__COUNT_PAGE = self.__GetCountPage()
        self.__URI = self.__SetUri(pageInfo['uri'])
        self.SHIFT = self.__START_NUM - 1

        keys = limit.split(',')

        pages = {}
        # 起始页
        pages['1'] = self.__GetStart()
        # 上一页
        pages['2'] = self.__GetPrev()
        # 分页
        pages['3'] = self.__GetPages()
        # 下一页
        pages['4'] = self.__GetNext()
        # 尾页
        pages['5'] = self.__GetEnd()

        # 当前显示页与总页数
        pages['6'] = "<span class='Pnumber'>" + str(self.__C_PAGE) + "/" + str(self.__COUNT_PAGE) + "</span>"
        # 本页显示开始与结束行
        pages['7'] = "<span class='Pline'>" + self.__FO + str(self.__START_NUM) + "-" + str(
            self.__END_NUM) + self.__LINE + "</span>"
        # 行数
        pages['8'] = "<span class='Pcount'>" + self.__COUNT_START + str(self.__COUNT_ROW) + self.__COUNT_END + "</span>"

        # 构造返回数据
        retuls = '<div>'
        for value in keys:
            retuls += pages[value]
        retuls += '</div>'

        # 返回分页数据
        return retuls

    def __GetEnd(self):
        # 构造尾页
        endStr = ""
        if self.__C_PAGE >= self.__COUNT_PAGE:
            endStr = ''
        else:
            if self.__RTURN_JS == "":
                endStr = "<a class='Pend' href='" + self.__URI + "p=" + str(
                    self.__COUNT_PAGE) + "'>" + self.__END + "</a>"
            else:
                endStr = "<a class='Pend' onclick='" + self.__RTURN_JS + "(" + str(
                    self.__COUNT_PAGE) + ")'>" + self.__END + "</a>"
        return endStr

    def __GetNext(self):
        # 构造下一页
        nextStr = ""
        if self.__C_PAGE >= self.__COUNT_PAGE:
            nextStr = ''
        else:
            if self.__RTURN_JS == "":
                nextStr = "<a class='Pnext' href='" + self.__URI + "p=" + str(
                    self.__C_PAGE + 1) + "'>" + self.__NEXT + "</a>"
            else:
                nextStr = "<a class='Pnext' onclick='" + self.__RTURN_JS + "(" + str(
                    self.__C_PAGE + 1) + ")'>" + self.__NEXT + "</a>"

        return nextStr

    def __GetPages(self):
        # 构造分页
        pages = ''
        num = 0
        # 当前页之前
        if (self.__COUNT_PAGE - self.__C_PAGE) < self.__LIST_NUM:
            num = self.__LIST_NUM + (self.__LIST_NUM - (self.__COUNT_PAGE - self.__C_PAGE))
        else:
            num = self.__LIST_NUM
        n = 0
        for i in range(num):
            n = num - i
            page = self.__C_PAGE - n
            if page > 0:
                if self.__RTURN_JS == "":
                    pages += "<a class='Pnum' href='" + self.__URI + "p=" + str(page) + "'>" + str(page) + "</a>"
                else:
                    pages += "<a class='Pnum' onclick='" + self.__RTURN_JS + "(" + str(page) + ")'>" + str(
                        page) + "</a>"

        # 当前页
        if self.__C_PAGE > 0:
            pages += "<span class='Pcurrent'>" + str(self.__C_PAGE) + "</span>"

        # 当前页之后
        if self.__C_PAGE <= self.__LIST_NUM:
            num = self.__LIST_NUM + (self.__LIST_NUM - self.__C_PAGE) + 1
        else:
            num = self.__LIST_NUM
        for i in range(num):
            if i == 0:
                continue
            page = self.__C_PAGE + i
            if page > self.__COUNT_PAGE:
                break
            if self.__RTURN_JS == "":
                pages += "<a class='Pnum' href='" + self.__URI + "p=" + str(page) + "'>" + str(page) + "</a>"
            else:
                pages += "<a class='Pnum' onclick='" + self.__RTURN_JS + "(" + str(page) + ")'>" + str(page) + "</a>"

        return pages

    def __GetPrev(self):
        # 构造上一页
        startStr = ''
        if self.__C_PAGE == 1:
            startStr = ''
        else:
            if self.__RTURN_JS == "":
                startStr = "<a class='Ppren' href='" + self.__URI + "p=" + str(
                    self.__C_PAGE - 1) + "'>" + self.__PREV + "</a>"
            else:
                startStr = "<a class='Ppren' onclick='" + self.__RTURN_JS + "(" + str(
                    self.__C_PAGE - 1) + ")'>" + self.__PREV + "</a>"
        return startStr

    def __GetStart(self):
        # 构造起始分页
        startStr = ''
        if self.__C_PAGE == 1:
            startStr = ''
        else:
            if self.__RTURN_JS == "":
                startStr = "<a class='Pstart' href='" + self.__URI + "p=1'>" + self.__START + "</a>"
            else:
                startStr = "<a class='Pstart' onclick='" + self.__RTURN_JS + "(1)'>" + self.__START + "</a>"
        return startStr

    def __GetCpage(self, p):
        # 取当前页
        if p:
            return p
        return 1

    def __StartRow(self):
        # 从多少行开始
        return (self.__C_PAGE - 1) * self.ROW + 1

    def __EndRow(self):
        # 从多少行结束
        if self.ROW > self.__COUNT_ROW:
            return self.__COUNT_ROW
        return self.__C_PAGE * self.ROW

    def __GetCountPage(self):
        # 取总页数
        return int(math.ceil(self.__COUNT_ROW / float(self.ROW)))

    def __SetUri(self, input):
        # 构造URI
        uri = '?'
        for key in input:
            if key == 'p':
                continue
            uri += key + '=' + input[key] + '&'
        return str(uri)

class files:
    # 检查敏感目录
    def __init__(self):
        self.BASE_DIR = os.path.dirname(os.path.abspath(__file__))

    def CheckDir(self, path):
        path = path.replace('//', '/')
        if path[-1:] == '/':
            path = path[:-1]
        nDirs = ('',
                 '/',
                 '/*',
                 '/www',
                 '/root',
                 '/boot',
                 '/bin',
                 '/etc',
                 '/home',
                 '/dev',
                 '/sbin',
                 '/var',
                 '/usr',
                 '/tmp',
                 '/sys',
                 '/proc',
                 '/media',
                 '/mnt',
                 '/opt',
                 '/lib',
                 '/srv',
                 '/selinux',
                 '/www/server',
                 '/www/server/data',
                 )
        for dir in nDirs:
            if (dir == path):
                return False
        return True


    def GetDiskInfo(self):
        # 取磁盘分区信息
        temp = ExecShell("df -h -P|grep '/'|grep -v tmpfs")[0]
        tempInodes = ExecShell("df -i -P|grep '/'|grep -v tmpfs")[0]
        temp1 = bytes.decode(temp).split('\n')
        tempInodes1 = bytes.decode(tempInodes).split('\n')
        diskInfo = []
        n = 0
        cuts = ['/mnt/cdrom', '/boot', '/boot/efi', '/dev', '/dev/shm', '/run/lock', '/run', '/run/shm', '/run/user']
        for tmp in temp1:
            n += 1
            inodes = tempInodes1[n - 1].split()
            disk = tmp.split()
            if len(disk) < 5: continue;
            if disk[1].find('M') != -1: continue;
            if disk[1].find('K') != -1: continue;
            if len(disk[5].split('/')) > 4: continue;
            if disk[5] in cuts: continue;
            arr = {}
            arr['path'] = disk[5]
            tmp1 = [disk[1], disk[2], disk[3], disk[4]]
            arr['size'] = tmp1
            arr['inodes'] = [inodes[1], inodes[2], inodes[3], inodes[4]]
            if disk[5] == '/':
                bootLog = '/tmp/panelBoot.pl'
                if disk[2].find('M') != -1:
                    if os.path.exists(bootLog): os.system('rm -f ' + bootLog)
            diskInfo.append(arr)
        return diskInfo

    # 检测文件名
    def CheckFileName(self, filename):
        nots = ['\\', '&', '*', '#', '@', '|']
        if filename.find('/') != -1: filename = filename.split('/')[-1]
        for n in nots:
            if n in filename: return False
        return True

    # 取文件/目录列表
    def GetDir(self, get,postdata):
        path = postdata.get('path')
        while path.startswith('/'):
            path = path[1:]
        path = os.path.join("/", path)
        if not os.path.exists(path): path = '/'

        dirnames = []
        filenames = []
        search = None
        if 'search' in get: search = get.get('search').strip().lower()

        # 包含分页类
        # 实例化分页类
        page = Page()
        info = {}
        info['count'] = self.GetFilesCount(path, search)
        info['row'] = 100
        info['p'] = 1
        if 'p' in get:
            try:
                info['p'] = int(get.get('p'))
            except:
                info['p'] = 0
        info['uri'] = {}
        info['return_js'] = ''
        if 'tojs' in get:
            info['return_js'] = get.get('tojs')
        if 'showRow' in get:
            info['row'] = int(get.get('showRow'))
        # 获取分页数据
        data = {}
        data['PAGE'] = page.GetPage(info, '1,2,3,4,5,6,7,8')
        i = 0
        n = 0
        for filename in os.listdir(path):
            if search:
                if filename.lower().find(search) == -1: continue
            i += 1
            if n >= page.ROW: break
            if i < page.SHIFT: continue

            try:
                filePath = os.path.join(path, filename).encode('utf8')
                link = ''
                if os.path.islink(filePath):
                    filePath = os.readlink(filePath)
                    link = ' -> ' + bytes.decode(filePath)
                    if not os.path.exists(filePath): filePath = os.path.join(path, bytes.decode(filePath))

                    if not os.path.exists(filePath): continue

                stat = os.stat(filePath)
                accept = str(oct(stat.st_mode)[-3:])
                mtime = str(int(stat.st_mtime))
                user = ''
                try:
                    user = pwd.getpwuid(stat.st_uid).pw_name
                except:
                    user = str(stat.st_uid)
                size = str(stat.st_size)
                if os.path.isdir(filePath):
                    dirnames.append(filename + ';' + size + ';' + mtime + ';' + accept + ';' + user + ';' + link)
                else:
                    filenames.append(filename + ';' + size + ';' + mtime + ';' + accept + ';' + user + ';' + link)
                n += 1
            except:
                continue

        data['DIR'] = sorted(dirnames)
        data['FILES'] = sorted(filenames)
        data['PATH'] = path
        if 'disk' in get:
            data['DISK'] = self.GetDiskInfo()
        return data

    # 计算文件数量
    def GetFilesCount(self, path, search):
        i = 0
        for name in os.listdir(path):
            if search:
                if name.lower().find(search) == -1: continue
            i += 1
        return i


    # 创建目录
    def CreateDir(self, get):
        path = bytes.decode(get.get('path').encode('utf-8'))
        while path.startswith('/'):
            path = path[1:]
        path = os.path.join("/", path)
        try:
            if not self.CheckFileName(path): return returnMsg(False, '目录名中不能包含特殊字符!')
            if os.path.exists(path):
                return returnMsg(False, 'DIR_EXISTS')
            os.makedirs(path)
            self.SetFileAccept(path)
            # public.WriteLog('TYPE_FILE','DIR_CREATE_SUCCESS',(get.path,))
            return returnMsg(True, 'DIR_CREATE_SUCCESS')
        except:
            return returnMsg(False, 'DIR_CREATE_ERR')

    # 删除目录
    def DeleteDir(self, get):
        path = bytes.decode(get.get('path').encode('utf-8'))
        while path.startswith('/'):
            path = path[1:]
        path = os.path.join("/", path)
        # if get.path.find('/www/wwwroot') == -1: return public.returnMsg(False,'此为演示服务器,禁止删除此目录!')
        if not os.path.exists(path):
            return returnMsg(False, 'DIR_NOT_EXISTS')

        # 检查是否敏感目录
        if not self.CheckDir(path):
            return returnMsg(False, 'FILE_DANGER')

        try:
            # 检查是否存在.user.ini
            if os.path.exists(os.path.join(path, '.user.ini')):
                os.system("chattr -i '" + os.path.join(path, '.user.ini'))
            if 'empty' in get:
                if not self.delete_empty(path): return returnMsg(False, 'DIR_ERR_NOT_EMPTY')

            shutil.rmtree(path)
            # public.WriteLog('TYPE_FILE','DIR_DEL_SUCCESS',(get.path,))
            return returnMsg(True, 'DIR_DEL_SUCCESS')
        except:
            return returnMsg(False, 'DIR_DEL_ERR')

    # 删除 空目录
    def delete_empty(self, path):
        for files in os.listdir(path):
            return False
        return True

    # 删除文件
    def DeleteFile(self, get):
        path = bytes.decode(get.get('path').encode('utf-8'))
        while path.startswith('/'):
            path = path[1:]
        path = os.path.join("/", path)
        if not os.path.exists(path):
            return returnMsg(False, 'FILE_NOT_EXISTS')

        # 检查是否为.user.ini
        if path.find('.user.ini'):
            os.system("chattr -i '" + path + "'")
        try:
            os.remove(path)
            # public.WriteLog('TYPE_FILE','FILE_DEL_SUCCESS',(get.path,))
            return returnMsg(True, 'FILE_DEL_SUCCESS')
        except:
            return returnMsg(False, 'FILE_DEL_ERR')

    # 复制文件
    def CopyFile(self, get):
        path = bytes.decode(get.get('sfile').encode('utf-8'))
        while path.startswith('/'):
            path = path[1:]
        sfile = os.path.join("/", path)

        path = bytes.decode(get.get('dfile').encode('utf-8'))
        while path.startswith('/'):
            path = path[1:]
        dfile = os.path.join("/", path)

        if not os.path.exists(sfile):
            return returnMsg(False, 'FILE_NOT_EXISTS')

        if os.path.isdir(sfile):
            return self.CopyDir(get)

        try:
            shutil.copyfile(sfile, dfile)
            # public.WriteLog('TYPE_FILE','FILE_COPY_SUCCESS',(get.sfile,get.dfile))
            stat = os.stat(sfile)
            os.chown(dfile, stat.st_uid, stat.st_gid)
            return returnMsg(True, 'FILE_COPY_SUCCESS')
        except:
            return returnMsg(False, 'FILE_COPY_ERR')

    # 复制文件夹
    def CopyDir(self, get):
        path = bytes.decode(get.get('sfile').encode('utf-8'))
        while path.startswith('/'):
            path = path[1:]
        sfile = os.path.join("/", path)

        path = bytes.decode(get.get('dfile').encode('utf-8'))
        while path.startswith('/'):
            path = path[1:]
        dfile = os.path.join("/", path)
        if not os.path.exists(sfile):
            return returnMsg(False, 'DIR_NOT_EXISTS')

        if os.path.exists(dfile):
            return returnMsg(False, 'DIR_EXISTS')


        try:
            shutil.copytree(sfile, dfile)
            stat = os.stat(sfile)
            os.chown(dfile, stat.st_uid, stat.st_gid)
            # public.WriteLog('TYPE_FILE','DIR_COPY_SUCCESS',(get.sfile,get.dfile))
            return returnMsg(True, 'DIR_COPY_SUCCESS')
        except:
            return returnMsg(False, 'DIR_COPY_ERR')

    # 移动文件或目录
    def MvFile(self, get):

        path = bytes.decode(get.get('sfile').encode('utf-8'))
        while path.startswith('/'):
            path = path[1:]
        sfile = os.path.join("/", path)

        path = bytes.decode(get.get('dfile').encode('utf-8'))
        while path.startswith('/'):
            path = path[1:]
        dfile = os.path.join("/", path)

        if not self.CheckFileName(dfile): return returnMsg(False, '文件名中不能包含特殊字符!')
        if not os.path.exists(sfile):
            return returnMsg(False, 'FILE_NOT_EXISTS')

        # if os.path.exists(get.dfile):
        #    return public.returnMsg(False,'FILE_EXISTS')

        if not self.CheckDir(sfile):
            return returnMsg(False, 'FILE_DANGER')

        try:
            shutil.move(sfile, dfile)
            # public.WriteLog('TYPE_FILE','MOVE_SUCCESS',(get.sfile,get.dfile))
            return returnMsg(True, 'MOVE_SUCCESS')
        except:
            return returnMsg(False, 'MOVE_ERR')

    # 检查文件是否存在
    def CheckExistsFiles(self, get):
        path = bytes.decode(get.get('dfile').encode('utf-8'))
        while path.startswith('/'):
            path = path[1:]
        dfile = os.path.join("/", path)

        data = []
        filesx = []
        if 'filename' in get:
            filesx.append(bytes.decode(get.get('filename').encode('utf-8')))
        for fn in filesx:
            filename = os.path.join(dfile, fn)
            if os.path.exists(filename):
                tmp = {}
                stat = os.stat(filename)
                tmp['filename'] = fn
                tmp['size'] = os.path.getsize(filename)
                tmp['mtime'] = str(int(stat.st_mtime))
                data.append(tmp)
        return data


    # 文件压缩
    def Zip(self, get):
        sfile = bytes.decode(get.get('sfile').encode('utf-8'))

        getpath = bytes.decode(get.get('dfile').encode('utf-8'))
        while getpath.startswith('/'):
            getpath = getpath[1:]
        dfile = os.path.join("/", getpath)

        getpath = bytes.decode(get.get('path').encode('utf-8'))
        while getpath.startswith('/'):
            getpath = getpath[1:]
        getpath = os.path.join("/", getpath)

        type = bytes.decode(get.get('type').encode('utf-8'))
        sfilefullpath = getpath

        if sfile.find(',') == -1:
            if not os.path.exists(os.path.join(getpath, sfile)): return returnMsg(False, 'FILE_NOT_EXISTS')
        try:
            tmps = '/tmp/panelExec.log'
            if type == 'zip':
                os.system("cd '" + getpath + "' && zip '" + dfile + "' -r '" + sfile + "' > " + tmps + " 2>&1")
            else:
                sfiles = ''
                for sfile in sfile.split(','):
                    if not sfile: continue
                    sfiles += " '" + sfile + "'"
                os.system("cd '" + getpath + "' && tar -zcvf '" + dfile + "' " + sfiles + " > " + tmps + " 2>&1")
            self.SetFileAccept(dfile)
            # public.WriteLog("TYPE_FILE", 'ZIP_SUCCESS',(get.sfile,get.dfile))
            return returnMsg(True, 'ZIP_SUCCESS')
        except:
            return returnMsg(False, 'ZIP_ERR')

    # 文件解压
    def UnZip(self, get):
        getpath = bytes.decode(get.get('dfile').encode('utf-8'))
        while getpath.startswith('/'):
            getpath = getpath[1:]
        dfile = os.path.join("/", getpath)

        getpath = bytes.decode(get.get('sfile').encode('utf-8'))
        while getpath.startswith('/'):
            getpath = getpath[1:]
        sfile = os.path.join("/", getpath)

        if not os.path.exists(sfile):
            return returnMsg(False, 'FILE_NOT_EXISTS')
        if 'password' in get: password = ''

        # try:
        coding = bytes.decode(get.get('coding').encode('utf-8'))
        if 'coding' in get: coding = 'UTF-8'
        tmps = '/tmp/panelExec.log'
        if sfile[-4:] == '.zip':
            os.system(
                "export LANG=\"zh_CN." + coding + "\" && unzip -P '" + password + "' -o '" + sfile + "' -d '" + dfile + "' > " + tmps + " 2>&1")
        elif sfile[-7:] == '.tar.gz' or sfile[-4:] == '.tgz':
            os.system("tar zxf '" + sfile + "' -C '" + dfile + "' > " + tmps + " 2>&1")
        else:
            os.system("gunzip -c " + sfile + " > " + sfile[:-3])
        # if self.CheckDir(dfile):
        # sites_path = public.M('config').where('id=?',(1)).getField('sites_path')
        # if get.dfile.find('/www/wwwroot') != -1 or get.dfile.find(sites_path) != -1: self.SetFileAccept(get.dfile)
        # public.WriteLog("TYPE_FILE", 'UNZIP_SUCCESS',(get.sfile,get.dfile))
        return returnMsg(True, 'UNZIP_SUCCESS')
        # except:
        #    return public.returnMsg(False,'文件解压失败!')

    # 获取文件/目录 权限信息
    def GetFileAccess(self, get):
        getpath = bytes.decode(get.get('filename').encode('utf-8'))
        while getpath.startswith('/'):
            getpath = getpath[1:]
        filename = os.path.join("/", getpath)
        data = {}
        try:
            stat = os.stat(filename)
            data['chmod'] = str(oct(stat.st_mode)[-3:])
            data['chown'] = pwd.getpwuid(stat.st_uid).pw_name
        except:
            data['chmod'] = 755
            data['chown'] = 'www'
        return data

    # 设置文件权限和所有者
    def SetFileAccess(self, get, all='-R'):
        getpath = bytes.decode(get.get('filename').encode('utf-8'))
        while getpath.startswith('/'):
            getpath = getpath[1:]
        filename = os.path.join("/", getpath)
        access = bytes.decode(get.get('access').encode('utf-8'))
        user = bytes.decode(get.get('user').encode('utf-8'))
        try:
            if not self.CheckDir(filename): return returnMsg(False, 'FILE_DANGER')
            if not os.path.exists(filename):
                return returnMsg(False, 'FILE_NOT_EXISTS')
            os.system('chmod ' + all + ' ' + access + " '" + filename + "'")
            os.system('chown ' + all + ' ' + user + ':' + user + " '" + filename + "'")
            # public.WriteLog('TYPE_FILE','FILE_ACCESS_SUCCESS',(filename,access,user))
            return returnMsg(True, 'SET_SUCCESS')
        except:
            return returnMsg(False, 'SET_ERROR')

    def SetFileAccept(self, filename):
        os.system('chown -R www:www ' + filename)
        os.system('chmod -R 755 ' + filename)

    # 取目录大小
    def GetDirSize(self, get):
        path = bytes.decode(get.get('path').encode('utf-8'))
        while path.startswith('/'):
            path = path[1:]
        path = os.path.join("/", path)
        tmp = ExecShell('du -sbh ' + path)
        return bytes.decode(tmp[0]).split()[0]


    # 批量操作
    def SetBatchData(self, get):
        sessiondata={}
        path = bytes.decode(get.get('path').encode('utf-8'))
        data = get.get('data')
        while path.startswith('/'):
            path = path[1:]
        path = os.path.join("/", path)
        type = bytes.decode(get.get('type').encode('utf-8'))
        if type == '1' or type == '2':
            sessiondata['data'] = json.dumps(data)
            sessiondata['path'] = path
            return sessiondata,returnMsg(True, 'FILE_ALL_TIPS')
        elif type == '3':
            for key in data:
                try:
                    access = bytes.decode(get.get('access').encode('utf-8'))
                    user = bytes.decode(get.get('user').encode('utf-8'))
                    filename = os.path.join(path ,key)
                    if not self.CheckDir(filename): return returnMsg(False, 'FILE_DANGER')
                    os.system('chmod -R ' + access + " '" + filename + "'")
                    os.system('chown -R ' + user + ':' + user + " '" + filename + "'")
                except:
                    continue
            # public.WriteLog('TYPE_FILE','FILE_ALL_ACCESS')
            return sessiondata,returnMsg(True, 'FILE_ALL_ACCESS')
        else:
            isRecyle = os.path.exists(os.path.join(self.BASE_DIR, 'recycle_bin.pl'))
            l = len(data)
            i = 0
            for key in data:
                try:
                    filename = os.path.join(path, key)
                    # path = filename
                    if not os.path.exists(filename): continue
                    i += 1
                    if os.path.isdir(filename):
                        if not self.CheckDir(filename): return returnMsg(False, 'FILE_DANGER')
                        shutil.rmtree(filename)
                    else:
                        if key == '.user.ini': os.system('chattr -i ' + filename)

                        os.remove(filename)
                except:
                    continue
            # public.WriteLog('TYPE_FILE','FILE_ALL_DEL')
            return sessiondata,returnMsg(True, 'FILE_ALL_DEL')

    # 批量粘贴
    def BatchPaste(self, get, request):
        path = bytes.decode(get.get('path').encode('utf-8'))
        while path.startswith('/'):
            path = path[1:]
        path = os.path.join("/", path)
        if not os.path.exists(path): path = '/'
        # if not self.CheckDir(path): return returnMsg(False,'FILE_DANGER')
        i = 0
        data = json.loads(request.get("data"))
        sesspath = request.get("path")
        l = len(data)
        type = bytes.decode(get.get('type').encode('utf-8'))
        if type == '1':
            for key in data:
                i += 1
                try:
                    sfile = os.path.join(sesspath, key)
                    dfile = os.path.join(path, key)
                    if os.path.isdir(sfile):
                        shutil.copytree(sfile, dfile)
                    else:
                        shutil.copyfile(sfile, dfile)
                    stat = os.stat(sfile)
                    os.chown(dfile, stat.st_uid, stat.st_gid)
                except:
                    continue
            # public.WriteLog('TYPE_FILE','FILE_ALL_COPY',(web.ctx.session.selected.path,get.path))
        else:
            for key in data:
                try:
                    i += 1
                    sfile = os.path.join(sesspath, key)
                    dfile = os.path.join(path, key)
                    shutil.move(sfile, dfile)
                except:
                    continue
            # public.WriteLog('TYPE_FILE','FILE_ALL_MOTE',(web.ctx.session.selected.path,get.path))
        errorCount = len(data) - i
        del (request['data'])
        del (request['path'])
        request['clean']=0
        return request,returnMsg(True, 'FILE_ALL', (str(i), str(errorCount)))


def decodes(info):
    jiemi = base64.urlsafe_b64decode(info)
    resultlist=json.loads(bytes.decode(jiemi, encoding='utf8'))
    return resultlist
def encodes(info):
    infostr = json.dumps(info)
    jiamistr = base64.urlsafe_b64encode(str.encode(infostr, encoding='utf8'))
    return jiamistr

def easys(aa):
# if __name__ == "__main__":
#     import sys
#     argvs = sys.argv
    inputs={}
    results={}
    sessiondata=[]
    dirlist=[]
    fileobj = files()
    inputs=decodes(aa)
    print("通过 easy 解密：",inputs)
    actions = inputs.get('actions')
    getdata =inputs.get('getdata')
    postdata = inputs.get('postdata')
    sessiondata = inputs.get('sessiondata')
    if actions == "GetDir":
        dirlist = fileobj.GetDir(getdata,postdata)
    elif actions == "MvFile":
        dirlist = fileobj.MvFile(postdata)
    elif actions == "DeleteFile":
        dirlist = fileobj.DeleteFile(postdata)
    elif actions == "Zip":
        dirlist = fileobj.Zip(postdata)
    elif actions == "UnZip":
        dirlist = fileobj.UnZip(postdata)
    elif actions == "GetDirSize":
        dirlist = fileobj.GetDirSize(postdata)
    elif actions == "GetFileAccess":
        dirlist = fileobj.GetFileAccess(postdata)
    elif actions == "SetFileAccess":
        dirlist = fileobj.SetFileAccess(postdata)
    elif actions == "CheckExistsFiles":
        dirlist = fileobj.CheckExistsFiles(postdata)
    elif actions == "CopyFile":
        dirlist = fileobj.CopyFile(postdata)
    elif actions == "SetBatchData":
        sessiondata,dirlist = fileobj.SetBatchData(postdata)
    elif actions == "BatchPaste":
        sessiondata,dirlist = fileobj.BatchPaste(postdata, sessiondata)
    elif actions == "DeleteDir":
        dirlist = fileobj.DeleteDir(postdata)
    elif actions == "CreateDir":
        dirlist = fileobj.CreateDir(postdata)
    results["sessiondata"]=sessiondata
    results["dirlist"]=dirlist
    print(encodes(results))
    return encodes(results)